﻿using AnnealingModel.Demo.Common;
using AnnealingModel.Demo.Common.Algorithm;
using AnnealingModel.Demo.Common.Function;
using ScottPlot;
using ScottPlot.Plottables;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace AnnealingModel.Demo
{
    public partial class FrmMain : Form
    {
        readonly BindingList<IdValues> _list = new BindingList<IdValues>();
        readonly Annealing _annealing;
        readonly System.Windows.Forms.Timer _timer;
        public FrmMain()
        {
            InitializeComponent();
            _timer = new System.Windows.Forms.Timer();
            _timer.Tick += _timer_Tick;

            _annealing = new Annealing();
            _timer.Interval = _annealing.Time;

            _annealing.ValueCallback += _annealing_ValueCallback;
            nudT.Value = (decimal)_annealing.T;
            nudIDt.Value = (decimal)_annealing.Dt;
            nudEps.Value = (decimal)_annealing.Eps;
            nudInnerLoop.Value = (decimal)_annealing.InnerLoop;
            nudTime.Value = (decimal)_annealing.Time;
            RegisterType();
            List<IdValues> list = new List<IdValues>();
            foreach (var item in _functionType)
            {
                list.Add(new IdValues
                {
                    Id = item.Key.GetHashCode().ToString(),
                    Value = item.Value.Item1.DisplayText
                });
            }
            cmbTargetFunction.DataSource = list;
            cmbTargetFunction.DisplayMember = "Value";
            cmbTargetFunction.ValueMember = "Id";
            cmbTargetFunction.SelectedIndex = 0;

            this.dataGridView1.AutoGenerateColumns = false;
            this.dataGridView1.DataSource = _list;

            
        }

        private void _timer_Tick(object? sender, EventArgs e)
        {
            if (!_data.TryDequeue(out var result)) return;

            (double x, double y) = result;
            if (_verticalLine != null)
                formsPlot1.Plot.Remove(_verticalLine);

            _verticalLine = formsPlot1.Plot.Add.VerticalLine(x);
            formsPlot1.Refresh();
            _list.Add(new IdValues
            {
                Id = _index.ToString(),
                Value = Math.Round(x, _annealing.Funtion.Accuracy).ToString(),
                Value2 = Math.Round(y, _annealing.Funtion.Accuracy).ToString()
            });
            _index++;
        }

        int _index = 1;
        VerticalLine _verticalLine;
        ConcurrentQueue<(double x, double y)> _data = new ConcurrentQueue<(double x, double y)>();
        private void _annealing_ValueCallback(double x, double y)
        {
            _data.Enqueue((x, y));
        }

        private void FrmMain_Load(object sender, EventArgs e)
        {

        }

        private void cmbTargetFunction_SelectedIndexChanged(object sender, EventArgs e)
        {
            IdValues? idValues = cmbTargetFunction.Items[cmbTargetFunction.SelectedIndex] as IdValues;
            if (!_functionType.ContainsKey((FunctionType)int.Parse(idValues.Id)))
            {
                MessageBox.Show($"不存在函数：{idValues.Value}");
                return;
            }
            Type type = _functionType[(FunctionType)int.Parse(idValues.Id)].Item2;
            FuntionBase? unaryFunction = Activator.CreateInstance(type) as FuntionBase;
            _annealing.Funtion = unaryFunction;

            nudLower.Value = (decimal)unaryFunction.Range.lower;
            nudUpper.Value = (decimal)unaryFunction.Range.upper;
            nudAccuracy.Value = (decimal)unaryFunction.Accuracy;
            rbtnMax.Checked = unaryFunction.Solve == Common.Function.SolveType.Max;
            rbtnMin.Checked = !rbtnMax.Checked;
            CreateData();
            BindFunction((unaryFunction.Range.lower + unaryFunction.Range.upper) / 2);
        }
        readonly ScottPlot.Color color = new ScottPlot.Color(255, 0, 0);
        List<double> xs = new List<double>();
        List<double> ys = new List<double>();
        private void CreateData()
        {
            xs.Clear();
            ys.Clear();
            int len = (int)nudDataCount.Value;
            double totalNumbers = _annealing.Funtion.Range.upper - _annealing.Funtion.Range.lower + 1;
            double interval = totalNumbers / len;
            for (double i = 0; i < len; i++)
            {
                double x = i * interval + _annealing.Funtion.Range.lower;
                xs.Add(x);
                ys.Add(_annealing.Funtion.Func(x));
            }
        }
        private void BindFunction(double x)
        {
            formsPlot1.Plot.Clear();

            var p = formsPlot1.Plot.Add.Scatter(xs, ys, color: color);
            p.LegendText = "函数曲线";
            p.MarkerSize = 3;
            formsPlot1.Plot.Axes.SetLimitsX(_annealing.Funtion.Range.lower, _annealing.Funtion.Range.upper);
            formsPlot1.Plot.Font.Set("微软雅黑");
            formsPlot1.Plot.XLabel("自变量");
            formsPlot1.Plot.YLabel("因变量");
            formsPlot1.Plot.Axes.SetLimitsY(ys.Min(), ys.Max());
            formsPlot1.Plot.Legend.IsVisible = true;
            formsPlot1.Plot.ShowLegend(Alignment.LowerRight);
            formsPlot1.Refresh();
        }
        private Dictionary<FunctionType, (DisplayTextAttribute, Type)> _functionType;
        private void RegisterType()
        {
            Type attributeType = typeof(DisplayTextAttribute);
            Type functionType = typeof(FuntionBase);
            _functionType = RegisterType(functionType, attributeType);
        }
        private static Dictionary<FunctionType, (DisplayTextAttribute, Type)> RegisterType(Type baseType, Type attributeType) => RegisterType(Assembly.GetEntryAssembly(), baseType, attributeType);
        private static Dictionary<FunctionType, (DisplayTextAttribute, Type)> RegisterType(Assembly assembly, Type baseType, Type attributeType)
        {
            return assembly.GetTypes().Where(t => t.IsSubclassOf(baseType) && t.GetCustomAttributes(attributeType, false).Any())
                .Select(t =>
                {
                    DisplayTextAttribute? sa = t.GetCustomAttribute(attributeType, false) as DisplayTextAttribute;

                    return (sa.Function, (sa, t));
                }).ToDictionary(t => t.Item1, t => t.Item2);
        }
        bool _isRun = false;
        private void btnRun_Click(object sender, EventArgs e)
        {
            if (_isRun)
            {
                MessageBox.Show("正在运行");
                return;
            }
            _isRun = true;
            _annealing.T = (uint)nudT.Value;
            _annealing.Dt = (double)nudIDt.Value;
            _annealing.Eps = (double)nudEps.Value;
            _annealing.InnerLoop = (int)nudInnerLoop.Value;
            _annealing.Time = (int)nudTime.Value;
            _annealing.Funtion.Range = ((double)nudLower.Value, (double)nudUpper.Value);
            _annealing.Funtion.Accuracy = (byte)nudAccuracy.Value;
            _annealing.Funtion.Solve = rbtnMax.Checked ? Common.Function.SolveType.Max : Common.Function.SolveType.Min;
            BindFunction((_annealing.Funtion.Range.lower + _annealing.Funtion.Range.upper) / 2);
            _list.Clear();
            _data.Clear();
            _index = 1;
            _timer.Start();
            Task.Factory.StartNew(() =>
            {
                _annealing.Run();
                while (_data.Count > 0)
                    Thread.Sleep(10);
                _timer.Stop();
                _isRun = false;
                MessageBox.Show("完成");
            });
        }

        private void nudTime_ValueChanged(object sender, EventArgs e)
        {
            _annealing.Time = (int)nudTime.Value;
            _timer.Interval = _annealing.Time;
        }
    }
    public class IdValues
    {
        public string Id { get; set; }
        public string Value { get; set; }
        public string Value2 { get; set; }
    }
}
